﻿#region MIT License

/*
 *  Copyright (c) 2010 Nathan Palmer
 *  http://www.nathanpalmer.com/
 * 
 *  Permission is hereby granted, free of charge, to any person obtaining a copy
 *  of this software and associated documentation files (the "Software"), to deal
 *  in the Software without restriction, including without limitation the rights
 *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 *  copies of the Software, and to permit persons to whom the Software is
 *  furnished to do so, subject to the following conditions:
 * 
 *  The above copyright notice and this permission notice shall be included in
 *  all copies or substantial portions of the Software.
 * 
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 *  THE SOFTWARE.
 */

#endregion

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Data;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Net.NetworkInformation;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using Castle.MicroKernel.Registration;
using Castle.Windsor;
using Castle.Windsor.Configuration.Interpreters;
using Common.Logging;
using Quartz;
using Quartz.Impl;
using Rhino.ServiceBus;
using Rhino.ServiceBus.Hosting;
using Rhino.ServiceBus.Impl;
using Rhino.ServiceBus.Internal;
using SimpleScheduler.Core.Common;
using SimpleScheduler.Core.Domain;
using Trigger = Quartz.Trigger;
using System.Configuration;

namespace SimpleScheduler.UI
{
    public partial class Interface : Form
    {
        private IScheduler scheduler;
        private static JobRowCollection jobRowCollection;
        Guid clientGuid = Guid.NewGuid();
        private string serverName = ConfigurationManager.AppSettings["Server"];
        private IStartableServiceBus bus;
        private bool busEnabled = false;
        private DataView jobView;
        private DataTable jobTable;
        private DataTable taskTable;

        public Interface()
        {
            InitializeComponent();
            InitializeData();
            InitializeDataGrid();

            //ILog log = LogManager.GetLogger(this.GetType());

            txtServer.Text = serverName;
            //splitContainer1.Panel2Collapsed = true;

            Shown += new EventHandler(Interface_Shown);
            FormClosing += new FormClosingEventHandler(Interface_FormClosing);
        }

        void EnableDisableControls(bool EnableDisable)
        {
            JobGrid.Enabled = EnableDisable;
            menuStrip1.Enabled = EnableDisable;
        }

        void Interface_Shown(object sender, EventArgs e)
        {
            EnableDisableControls(false);
            
            if (serverName.Length > 0)
                bgInitialize.RunWorkerAsync();
        }

        private void bgInitialize_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
        {
            //InitializeBus();
            try
            {
                InitializeQuartz();
                RefreshJobs();
            }
            catch(Exception ex)
            {
                MessageBox.Show(string.Format("Unable to connect to server\r\n\r\n{0}", ex.Message));
            }
        }

        private void bgInitialize_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
        {
            UpdateStatus("Idle");
            EnableDisableControls(true);
        }

        void Interface_FormClosing(object sender, FormClosingEventArgs e)
        {
            var basePath = new DirectoryInfo(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location));

            var esentPath = new DirectoryInfo(Path.Combine(basePath.FullName, clientGuid.ToString().Replace("-","") + ".esent"));
            if (esentPath.Exists)
            {
                esentPath.Delete(true);
            }

            var esentSubPath = new DirectoryInfo(Path.Combine(basePath.FullName, clientGuid.ToString().Replace("-", "") + "_subscriptions.esent"));
            if (esentSubPath.Exists)
            {
                esentSubPath.Delete(true);
            }
        }

        private void InitializeBus()
        {
            busEnabled = true;
            UpdateStatus("Initializing service bus");
            //var container = new WindsorContainer(new XmlInterpreter());
            //container.Kernel.AddFacility("rhino.esb", new RhinoServiceBusFacility());

            //var subscriber = new RemoteAppDomainHost(typeof (SimpleSchedulerServerBootstrapper)).Configuration(Assembly.GetExecutingAssembly().N )
            //subscriber.Start();
            
            var clientName = clientGuid.ToString().Replace("-","");
            int port = GetUnusedPort();
            string ip = GetCurrentIP();

            //applicationHost = new DefaultHost();
            //applicationHost.BusConfiguration(
            //    c => c.Bus("rhino.queues://" + ip + ":" + port + "/simplescheduler_client", clientName)
            //             .Receive("SimpleScheduler.Core.Domain",
            //                      "rhino.queues://" + serverName + "/simplescheduler_server"));
            //bus = applicationHost.Container.Resolve<IServiceBus>();
            //applicationHost.Start<SimpleSchedulerServerBootstrapper>();            
            
            var container = new WindsorContainer(new XmlInterpreter());
            container.Kernel.AddFacility("rhino.esb", new RhinoServiceBusFacility());
            container.Register(
                AllTypes.FromAssembly(Assembly.GetExecutingAssembly())
                    .BasedOn(typeof(IMessageConsumer))
                );

            bus = container.Resolve<IStartableServiceBus>();
            bus.Start();
        }

        private string GetCurrentIP()
        {
            string host = System.Net.Dns.GetHostName();
            foreach(var address in System.Net.Dns.GetHostEntry(host).AddressList)
            {
                if (!address.ToString().StartsWith("223."))
                {
                    return address.ToString();
                }
                //Console.WriteLine(address.ToString());
            }

            return "";
        }

        private int GetUnusedPort()
        {
            for (int i = 50000; i < 50100; i++)
            {
                if (IsPortAvailable(i))
                {
                    return i;
                }
            }

            throw new Exception("Unable to find a port to bind too");
        }

        private bool IsPortAvailable(int port)
        {
            bool isAvailable = true;

            // Evaluate current system tcp connections. This is the same information provided
            // by the netstat command line application, just in .Net strongly-typed object
            // form.  We will look through the list, and if our port we would like to use
            // in our TcpClient is occupied, we will set isAvailable to false.
            IPGlobalProperties ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties();
            TcpConnectionInformation[] tcpConnInfoArray = ipGlobalProperties.GetActiveTcpConnections();

            foreach (TcpConnectionInformation tcpi in tcpConnInfoArray)
            {
                if (tcpi.LocalEndPoint.Port == port)
                {
                    isAvailable = false;
                    break;
                }
            }

            return isAvailable;
        }

        private void InitializeDataGrid()
        {
            InitializeJobGrid();
            InitializeTaskGrid();
        }

        private void InitializeJobGrid()
        {
            JobGrid.AllowUserToAddRows = false;
            JobGrid.AllowUserToDeleteRows = false;
            JobGrid.AllowUserToOrderColumns = false;
            JobGrid.AllowUserToResizeColumns = true;
            JobGrid.AllowUserToResizeRows = false;
            JobGrid.RowHeadersVisible = false;
            JobGrid.EditMode = DataGridViewEditMode.EditProgrammatically;
            JobGrid.AutoSize = true;
            //dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;

            JobGrid.BackgroundColor = Color.White;

            jobRowCollection = new JobRowCollection();

            JobGrid.AutoGenerateColumns = false;
            JobGrid.Columns.Add("Key", "Key");
            JobGrid.Columns[JobGrid.Columns.Count - 1].Visible = false;
            JobGrid.Columns[JobGrid.Columns.Count - 1].DataPropertyName = "Key";
            JobGrid.Columns.Add("Group", "Group");
            JobGrid.Columns[JobGrid.Columns.Count - 1].Width = 50;
            JobGrid.Columns[JobGrid.Columns.Count - 1].DataPropertyName = "Group";
            JobGrid.Columns.Add("Name", "Name");
            JobGrid.Columns[JobGrid.Columns.Count - 1].Width = 200;
            JobGrid.Columns[JobGrid.Columns.Count - 1].DataPropertyName = "Name";
            JobGrid.Columns.Add("Status", "Status");
            JobGrid.Columns[JobGrid.Columns.Count - 1].Width = 100;
            JobGrid.Columns[JobGrid.Columns.Count - 1].DataPropertyName = "Status";
            JobGrid.Columns.Add("Schedule", "Schedule");
            JobGrid.Columns[JobGrid.Columns.Count - 1].Width = 150;
            JobGrid.Columns[JobGrid.Columns.Count - 1].DataPropertyName = "Schedule";
            JobGrid.Columns.Add("NextRunTime", "Next Run Time");
            JobGrid.Columns[JobGrid.Columns.Count - 1].Width = 135;
            JobGrid.Columns[JobGrid.Columns.Count - 1].DataPropertyName = "NextRunTime";
            JobGrid.Columns.Add("LastRunTime", "Last Run Time");
            JobGrid.Columns[JobGrid.Columns.Count - 1].Width = 135;
            JobGrid.Columns[JobGrid.Columns.Count - 1].DataPropertyName = "LastRunTime";
            
            JobGrid.DataSource = jobView;
        }

        private void InitializeTaskGrid()
        {
            TaskGrid.AllowUserToAddRows = false;
            TaskGrid.AllowUserToDeleteRows = false;
            TaskGrid.AllowUserToOrderColumns = false;
            TaskGrid.AllowUserToResizeColumns = true;
            TaskGrid.AllowUserToResizeRows = false;
            TaskGrid.RowHeadersVisible = false;
            TaskGrid.EditMode = DataGridViewEditMode.EditProgrammatically;
            TaskGrid.AutoSize = true;
            TaskGrid.SelectionMode = DataGridViewSelectionMode.FullRowSelect;
            //dataGridView1.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;

            TaskGrid.BackgroundColor = Color.White;

            TaskGrid.AutoGenerateColumns = false;
            TaskGrid.Columns.Add("Name", "Name");
            TaskGrid.Columns[TaskGrid.Columns.Count - 1].Width = 150;
            TaskGrid.Columns[TaskGrid.Columns.Count - 1].DataPropertyName = "Name";
            TaskGrid.Columns.Add("LastResult", "Last Result");
            TaskGrid.Columns[TaskGrid.Columns.Count - 1].Width = 100;
            TaskGrid.Columns[TaskGrid.Columns.Count - 1].DataPropertyName = "LastResult";

            TaskGrid.DataSource = taskTable;
        }

        private void InitializeData()
        {
            jobTable = new DataTable();
            jobTable.Columns.Add("Key");
            jobTable.Columns.Add("Group");
            jobTable.Columns.Add("Name");
            jobTable.Columns.Add("Status");
            jobTable.Columns.Add("Schedule");
            jobTable.Columns.Add("NextRunTime");
            jobTable.Columns.Add("LastRunTime");            

            jobView = new DataView(jobTable);

            taskTable = new DataTable();
            taskTable.Columns.Add("Name");
            taskTable.Columns.Add("LastResult");

            //jobView = new DataView(jobTable);
        }

        private void RefreshTasks(JobDetail JobDetail)
        {
            if (TaskGrid.InvokeRequired)
            {
                TaskGrid.Invoke(new Action(() => RefreshTasks(JobDetail)));
            }
            else
            {
                taskTable.Rows.Clear();

                TaskCollection tasks = JobDetail.JobDataMap["Tasks"] as TaskCollection ?? new TaskCollection();
                Dictionary<string, TaskResult> taskResults = JobDetail.JobDataMap["TaskResults"] as Dictionary<string, TaskResult> ?? new Dictionary<string, TaskResult>();
                
                foreach(var task in tasks)
                {
                    if (task.Name != null)
                    {
                        var taskResult = (taskResults.ContainsKey(task.Name) ? taskResults[task.Name] : new TaskResult());

                        var dataRow = taskTable.NewRow();

                        dataRow["Name"] = task.Name;
                        dataRow["LastResult"] = taskResult.Status;

                        taskTable.Rows.Add(dataRow);
                    }
                }
            }
        }

        private void RefreshJobs(int Delay)
        {
            new Thread((d) =>
                           {
                               Thread.Sleep((int) d);
                               RefreshJobs();
                           }).Start(Delay);
        }

        private void RefreshJobs()
        {
            if (JobGrid.InvokeRequired)
            {
                JobGrid.Invoke(new Action(RefreshJobs));
            }
            else
            {
                UpdateStatus("Refreshing jobs");

                jobTable.Rows.Clear();

                var executing = scheduler.GetCurrentlyExecutingJobs().Cast<JobExecutionContext>();

                foreach (var groupName in scheduler.JobGroupNames)
                {
                    foreach (var jobName in scheduler.GetJobNames(groupName))
                    {
                        var jobDetail = scheduler.GetJobDetail(jobName, groupName);
                        var jobState = executing.Any(j => j.JobDetail.Name == jobName && j.JobDetail.Group == groupName) ? JobState.Running : JobState.Idle;
                        //var jobState = JobState.Idle;
                        AddJob(jobDetail, jobState);
                        RequestJobStatus(jobDetail);
                    }
                }

                UpdateStatus("Idle");
            }
        }

        private void RequestJobStatus(JobDetail JobDetail)
        {
            if (busEnabled)
            {
                JobStateRequest jobStateRequest = new JobStateRequest()
                                                      {
                                                          JobName = JobDetail.Name,
                                                          GroupName = JobDetail.Group
                                                      };

                bus.Send(jobStateRequest);
            }
        }

        private delegate DataGridViewRow FindJobRowDelegate(string Key);
        private DataGridViewRow FindJobRow(string Key)
        {
            if (JobGrid.InvokeRequired)
            {
                return (DataGridViewRow) JobGrid.Invoke(new FindJobRowDelegate(FindJobRow), Key);
            }

            var row = JobGrid.Rows.Cast<DataGridViewRow>().Where(x => x.Cells["Key"].Value.ToString() == Key).FirstOrDefault();
            return row;
        }

        public void RemoveJob(string jobName, string groupName)
        {
            var row = FindJobRow(JobHelpers.GenerateJobKey(jobName, groupName));

            if (row != null)
            {
                RemoveJobRow(row);
            }
        }

        public void AddJob(string JobName, string GroupName)
        {
            var jobDetail = scheduler.GetJobDetail(JobName, GroupName);            
            if (jobDetail != null)
            {
                var jobState = (scheduler.GetCurrentlyExecutingJobs() as IList<JobExecutionContext>).Any(j => j.JobDetail.Name == JobName && j.JobDetail.Group == GroupName) ? JobState.Running : JobState.Idle;
                AddJob(jobDetail, jobState);
                RequestJobStatus(jobDetail);
            }
        }
        
        public void AddJob(JobDetail Job, JobState State)
        {
            var jobKey = JobHelpers.GenerateJobKey(Job.Name, Job.Group);
            var row = FindJobRow(jobKey);

            if (row == null)
            {
                row = AddJobRow(jobKey, Job.Name, Job.Group);
            }

            UpdateJob(Job, State, row);
        }

        private delegate DataGridViewRow AddJobRowDelegate(string Key, string Job, string Group);
        private DataGridViewRow AddJobRow(string Key, string Job, string Group)
        {
            if (JobGrid.InvokeRequired)
            {
                return (DataGridViewRow) JobGrid.Invoke(new AddJobRowDelegate(AddJobRow), Key, Job, Group);
            }

            var dataRow = jobTable.NewRow();
            dataRow["Key"] = Key;
            dataRow["Name"] = Job;
            dataRow["Group"] = Group;
            jobTable.Rows.Add(dataRow);

            return FindJobRow(Key);
        }

        private void RemoveJobRow(DataGridViewRow Row)
        {
            if (JobGrid.InvokeRequired)
            {
                JobGrid.Invoke(new MethodInvoker(() => RemoveJobRow(Row)));
            }
            else
            {
                JobGrid.Rows.Remove(Row);
            }
        }

        private void UpdateJob(JobDetail Job, JobState State, DataGridViewRow row)
        {
            row.Cells["Key"].Value = JobHelpers.GenerateJobKey(Job.Name, Job.Group);
            row.Cells["Group"].Value = Job.Group;
            row.Cells["Name"].Value = Job.Name;
            row.Cells["Status"].Value = Job.JobDataMap["Enabled"].ToString() == Boolean.TrueString ? State.ToString("g") : "Disabled";

            Trigger[] triggers = scheduler.GetTriggersOfJob(Job.Name, Job.Group).ToArray();
            if (triggers.Length == 1)
            {
                var trigger = triggers[0];

                DateTimeOffset? nextRunTime = trigger.GetNextFireTimeUtc();
                if (nextRunTime.HasValue)
                {
                    row.Cells["NextRunTime"].Value = nextRunTime.Value.ToLocalTime().ToString("hh:mm:ss tt MM/dd/yyyy");
                }

                DateTimeOffset? lastRunTime = trigger.GetPreviousFireTimeUtc();
                if (lastRunTime.HasValue)
                {
                    row.Cells["LastRunTime"].Value = lastRunTime.Value.ToLocalTime().ToString("hh:mm:ss tt MM/dd/yyyy");
                }

                if (trigger is SimpleTrigger)
                {
                    var simpleTrigger = (SimpleTrigger) trigger;
                    StringBuilder sb = new StringBuilder();
                    sb.Append("Runs every ");
                    if (simpleTrigger.RepeatInterval.Hours > 0)
                    {
                        sb.Append(string.Format("{0:D2} hrs ", simpleTrigger.RepeatInterval.Hours));
                    }
                    if (simpleTrigger.RepeatInterval.Minutes > 0)
                    {
                        sb.Append(string.Format("{0:D2} mins ", simpleTrigger.RepeatInterval.Minutes));
                    }
                    if (simpleTrigger.RepeatInterval.Seconds > 0)
                    {
                        sb.Append(string.Format("{0:D2} secs ", simpleTrigger.RepeatInterval.Seconds));
                    }
                    row.Cells["Schedule"].Value = sb.ToString();
                }
                else if (trigger is CronTrigger)
                {
                    row.Cells["Schedule"].Value = "Cron";
                }
            }
            else if (triggers.Length == 0)
            {
                row.Cells["Schedule"].Value = "No Triggers";
            }
            else
            {
                row.Cells["Schedule"].Value = "Multiple Triggers";
            }
        }

        public void UpdateState(string Key, JobState State)
        {
            if (JobGrid.InvokeRequired)
            {
                JobGrid.Invoke(new Action(() => UpdateState(Key, State)));
            }
            else
            {
                var row = FindJobRow(Key);
                var jobDetail = scheduler.GetJobDetail(row.Cells["Name"].Value.ToString(), row.Cells["Group"].Value.ToString());
                UpdateJob(jobDetail, State, row);
            }
        }


        private void InitializeQuartz()
        {
            UpdateStatus("Initializing Quartz");            

            NameValueCollection properties = new NameValueCollection();
            properties["quartz.scheduler.instanceName"] = serverName;

            // set thread pool info
            properties["quartz.threadPool.type"] = "Quartz.Simpl.SimpleThreadPool, Quartz";
            properties["quartz.threadPool.threadCount"] = "5";
            properties["quartz.threadPool.threadPriority"] = "Normal";

            // set remoting expoter
            properties["quartz.scheduler.proxy"] = "true";
            properties["quartz.scheduler.proxy.address"] = "tcp://" + serverName + ":555/QuartzScheduler";

            ISchedulerFactory sf = new StdSchedulerFactory(properties);
            scheduler = sf.GetScheduler();
            
            //sched.AddJobListener(this);
        }

        private void UpdateStatus(string Status)
        {
            if (statusStrip1.InvokeRequired)
            {
                statusStrip1.Invoke(new Action(() => UpdateStatus(Status)));
            }
            else
            {
                tsStatus.Text = Status;
            }
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            tsTime.Text = DateTime.Now.ToString("MM/dd/yyyy hh:mm:ss tt");
        }

        private void dataGridView1_CellClick(object sender, DataGridViewCellEventArgs e)
        {
            
        }

        private void runToolStripMenuItem_Click(object sender, EventArgs e)
        {            
            if (JobGrid.SelectedRows.Count == 1)
            {
                var row = JobGrid.SelectedRows[0];
                string groupName = (string) row.Cells["Group"].Value;
                string jobName = (string) row.Cells["Name"].Value;
                scheduler.TriggerJob(jobName, groupName);

                //HACK: Wait 1 second to the server shows the job as running
                RefreshJobs(1000);
            }
        }

        private void stopToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (JobGrid.SelectedRows.Count == 1)
            {
                var row = JobGrid.SelectedRows[0];
                string groupName = (string)row.Cells["Group"].Value;
                string jobName = (string)row.Cells["Name"].Value;
                scheduler.Interrupt(jobName, groupName);

                //HACK: Wait 3 seconds to the server has time to interrupt the job
                RefreshJobs(3000);
            }
        }

        private void disableToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (JobGrid.SelectedRows.Count == 1)
            {
                var row = JobGrid.SelectedRows[0];
                string groupName = (string)row.Cells["Group"].Value;
                string jobName = (string)row.Cells["Name"].Value;
                var jobDetail = scheduler.GetJobDetail(jobName, groupName);
                jobDetail.JobDataMap["Enabled"] = false;
                scheduler.AddJob(jobDetail, true);
                scheduler.PauseJob(jobName, groupName);

                //HACK: Wait 3 seconds to the server has time to disable the job
                RefreshJobs(3000);
            }
        }

        private void enableToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (JobGrid.SelectedRows.Count == 1)
            {
                var row = JobGrid.SelectedRows[0];
                string groupName = (string)row.Cells["Group"].Value;
                string jobName = (string)row.Cells["Name"].Value;
                var jobDetail = scheduler.GetJobDetail(jobName, groupName);
                jobDetail.JobDataMap["Enabled"] = true;
                scheduler.AddJob(jobDetail, true);
                scheduler.PauseJob(jobName, groupName);

                //HACK: Wait 3 seconds to the server has time to enable the job
                RefreshJobs(3000);
            }
        }

        private void dataGridView1_CellMouseDown(object sender, DataGridViewCellMouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                JobGrid.CurrentCell = JobGrid[e.ColumnIndex, e.RowIndex];

                var row = JobGrid.SelectedRows[0];
                if ((string)row.Cells["Status"].Value == "Disabled")
                {
                    enableToolStripMenuItem.Enabled = true;
                    disableToolStripMenuItem.Enabled = false;
                }
                else
                {
                    enableToolStripMenuItem.Enabled = false;
                    disableToolStripMenuItem.Enabled = true;
                }                
            }
        }

        private void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            Close();
        }

        private void Interface_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.F5)
            {
                RefreshJobs();
            }
        }

        private void txtServer_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter)
            {
                bgInitialize.RunWorkerAsync();
            }
        }

        private void txtFilter_KeyDown(object sender, KeyEventArgs e)
        {
            
        }

        private void txtFilter_KeyPress(object sender, KeyPressEventArgs e)
        {
            
        }

        private void txtFilter_KeyUp(object sender, KeyEventArgs e)
        {
            if (txtFilter.Text.Length > 0)
            {
                string rowFilter = "";
                foreach (var word in txtFilter.Text.Split(' '))
                {
                    if (rowFilter.Length > 0) rowFilter += " AND ";
                    rowFilter += string.Format("(Group LIKE '{0}*' OR Name LIKE '{0}*')", word);
                }

                jobView.RowFilter = rowFilter;
            }
            else
            {
                jobView.RowFilter = "";
            }
        }

        private void aboutToolStripMenuItem_Click(object sender, EventArgs e)
        {
            using (AboutBox about = new AboutBox())
            {
                about.ShowDialog(this);
            }
        }

        private void JobGrid_SelectionChanged(object sender, EventArgs e)
        {
            if (JobGrid.SelectedRows.Count == 1)
            {
                var jobName = JobGrid.SelectedRows[0].Cells["Name"].Value.ToString();
                var groupName = JobGrid.SelectedRows[0].Cells["Group"].Value.ToString();

                if (jobName.Length > 0 && groupName.Length > 0)
                {
                    var jobDetail = scheduler.GetJobDetail(jobName, groupName);
                    RefreshTasks(jobDetail);
                }
            }
        }

        private void TaskGrid_SelectionChanged(object sender, EventArgs e)
        {
            if (JobGrid.SelectedRows.Count == 1 && TaskGrid.SelectedRows.Count == 1)
            {
                var taskName = TaskGrid.SelectedRows[0].Cells["Name"].Value.ToString();
                var jobName = JobGrid.SelectedRows[0].Cells["Name"].Value.ToString();
                var groupName = JobGrid.SelectedRows[0].Cells["Group"].Value.ToString();

                var jobDetail = scheduler.GetJobDetail(jobName, groupName);
                Dictionary<string, TaskResult> taskResults = jobDetail.JobDataMap["TaskResults"] as Dictionary<string, TaskResult> ?? new Dictionary<string, TaskResult>();
                var taskOutput = taskResults.ContainsKey(taskName) ? taskResults[taskName].Output : "";

                textBox1.Text = taskOutput;
            }
        }

       

     
    }
}